#include<Python.h>
#include"structmember.h"
#include"SimpleDecoder.h"

#include<mad.h>
#include<pthread.h>
static void * mad_thread(void *args);
static enum mad_flow mad_input(void *arg, struct mad_stream *stream);
static enum mad_flow mad_output(void *data, struct mad_header const *header, struct mad_pcm *pcm);
static enum mad_flow mad_error(void *data, struct mad_stream *stream, struct mad_frame *frame) ;

//extern void SDecoder_dealloc(SDecoder *self);
//extern PyObject * SDecoder_new(PyTypeObject *type, PyObject *args, PyObject *kwds);
//extern int SDecoder_init(SDecoder *self, PyObject *args, PyObject *kwds);
//extern PyObject * SDecoder_config(SDecoder *self, PyObject *args, PyObject *kwds);
//extern PyObject * SDecoder_run(SDecoder *self, PyObject *args, PyObject *kwds);
//extern PyObject * SDecoder_getPCM(SDecoder *self, PyObject *args);
//extern PyObject * SDecoder_name(SDecoder *self);


static void
SDecoder_dealloc(SDecoder *self)
{
	free(self->data[0]);
	Py_TYPE(self)->tp_free((PyObject*)self);
}

static PyObject *
SDecoder_new(PyTypeObject *type, PyObject *args, PyObject *kwds)
{
	SDecoder *self;

	self = (SDecoder*) type->tp_alloc(type, 0);
	if (self != NULL) {
		const int bufsiz = 4098;
		void *tmp;

		/* allocating data space.  */
		tmp = malloc(2*sizeof(Data)+ 3*bufsiz);
		if (tmp==NULL) {
			return NULL;
		}

		/* initialize data space. */
		self->data[0] = tmp;
		self->data[1] = tmp+sizeof(Data);
		initialize_Data(self->data[0]);
		initialize_Data(self->data[1]);

		self->data[0]->data = tmp+2*sizeof(Data);
		self->data[1]->data = tmp+2*sizeof(Data)+bufsiz;
		self->data[0]->allocated_size = bufsiz;
		self->data[1]->allocated_size = bufsiz;
		self->buff = tmp+2*sizeof(Data)+2*bufsiz;
	}

	return (PyObject*) self;
}

static int
SDecoder_init(SDecoder *self, PyObject *args, PyObject *kwds)
{
	PyObject *file=NULL;

	static char *kwlist[] = {"file", "number", NULL};
	int bufsiz = 4098;
	void *tmp;

	if (!PyArg_ParseTupleAndKeywords(args, kwds, "O|i", kwlist,
				&file, &bufsiz)) 
		return -1;

	if (file) {
		tmp = self->FileObject;
		Py_INCREF(file);
		self->FileObject = file;
		Py_XDECREF(tmp);
	}

	/* allocating data space.  */
	if (self->data[0]) free(self->data[0]);
	tmp = malloc(2*sizeof(Data)+3*bufsiz);
	if (tmp==NULL) {
		return -1;
	}

	/* initialize data space. */
	self->data[0] = tmp;
	self->data[1] = tmp+sizeof(Data);
	initialize_Data(self->data[0]);
	initialize_Data(self->data[1]);

	self->data[0]->data = tmp+2*sizeof(Data);
	self->data[1]->data = tmp+2*sizeof(Data)+bufsiz;
	self->data[0]->allocated_size = bufsiz;
	self->data[1]->allocated_size = bufsiz;
	self->bufsiz = bufsiz;
	self->buff = tmp+2*sizeof(Data)+2*bufsiz;

	/* destruct old decoder.  */
	// TODO
	//if (self->decoder)
		//mad_decoder_finish(&self->decoder);

	/* open new decoder.  */
	mad_decoder_init(&self->decoder, self,
			mad_input, 0 /* header */, 0 /* filter */, mad_output,
			mad_error, 0 /* message */);

	return 0;
}

static PyObject *
SDecoder_config(SDecoder *self, PyObject *args, PyObject *kwds)
{
	Py_INCREF(Py_None);
	return Py_None;
}

static PyObject *
SDecoder_run(SDecoder *self, PyObject *args, PyObject *kwds)
{
	printf("in the SDecoder_run function.\n");
	pthread_create(&self->thread, NULL, mad_thread, self);
	Py_INCREF(Py_None);
	return Py_None;
}

static PyObject *
SDecoder_getPCM(SDecoder *self, PyObject *args)
{
	int size = 128;
	PyObject *ret;

	if (!PyArg_ParseTuple(args, "|i", &size))
		return NULL;


	Data *data = self->data[self->readfrom];

	pthread_mutex_lock(&data->mutex); // enter the critical region

	/* wait until the buffer becomes readable!  */
	while(data->state!=READABLE) {
		pthread_cond_wait(&data->cond, &data->mutex);
		//if (!runnable) {
			//pthread_mutex_unlock(&data->mutex); // leave the critical region
			////break;
		//}
	}

	ret = Py_BuildValue("y#", data->data+data->cur_offset, data->size-data->cur_offset); //buffの中身は別の場所にコピーされる

	data->cur_offset = 0;
	data->state = WRITABLE;
	self->readfrom = 1 - self->readfrom;
	pthread_cond_signal(&data->cond);
	//printf("consumer: emptied out data space.\n");
	pthread_mutex_unlock(&data->mutex); // leave the critical region

	if (data->eof || data->size==0) {
		// TODO
		printf("in getPCM: reach EOF\n");
		Py_DECREF(ret);
		Py_INCREF(Py_None);

		return Py_None;
	}

	return ret;
}

static void *
mad_thread(void *args)
{
	int result;
	SDecoder *self = args;

	/* decoder starts.  */
	result = mad_decoder_run(&self->decoder, MAD_DECODER_MODE_SYNC);
	/* decoder finished.  */

	Data *data = self->data[self->writeto];
	pthread_mutex_lock(&data->mutex); // enter the critical region

	/* wait until the buffer becomes readable!  */
	while (data->state!=WRITABLE) {
		pthread_cond_wait(&data->cond, &data->mutex);
	}

	data->cur_offset = 0;
	data->state = READABLE;
	data->size = 0;
	pthread_cond_signal(&data->cond);
	pthread_mutex_unlock(&data->mutex); // leave the critical region

	return NULL;
}

static
enum mad_flow mad_input(void *arg,
		    struct mad_stream *stream)
{
	SDecoder *self = (SDecoder*)arg;
	PyObject *byteObj;
	char *data;
	int datasize;
	int remaining_bytes;
	//struct buffer *buffer = data;

	/* Reserve some data which have not been processed yet.  */
	if (stream->next_frame) {
		//printf("next_frame=%p, stream->bufend=%p\n", stream->next_frame, stream->bufend);
		remaining_bytes = stream->bufend - stream->next_frame;
		if (remaining_bytes > self->bufsiz || remaining_bytes<=0) {
			//printf("mad_input: error too large remaining_bytes");
			//printf("%d bytes remianing!\n", remaining_bytes);
			return MAD_FLOW_STOP;
		}
		memmove(self->buff, stream->next_frame, remaining_bytes);
	}

	/* Read data from FileObject through Python Method.  */
	byteObj = PyObject_CallMethod(self->FileObject, "read", "i", self->bufsiz - remaining_bytes);
	if (-1==PyObject_AsReadBuffer(byteObj, (const void**)&data, &datasize)) {
		//printf("mad_input: error FLOW_STOP\n");
		return MAD_FLOW_STOP;
	}
	if (datasize==0) {
		return MAD_FLOW_STOP;
	}

	/* Copy these data to the buffer.  */
	memmove(self->buff+remaining_bytes, data, datasize);
	/* Set buffer.  */
	mad_stream_buffer(stream, (unsigned char const*)self->buff, remaining_bytes + datasize);

	return MAD_FLOW_CONTINUE;
}

static inline
signed int scale(mad_fixed_t sample)
{
	/* round */
	sample += (1L << (MAD_F_FRACBITS - 16));

	/* clip */
	if (sample >= MAD_F_ONE)
		sample = MAD_F_ONE - 1;
	else if (sample < -MAD_F_ONE)
		sample = -MAD_F_ONE;

	/* quantize */
	return sample >> (MAD_F_FRACBITS + 1 - 16);
}

static
enum mad_flow mad_output(void *data,
                     struct mad_header const *header,
                     struct mad_pcm *pcm)
{
	SDecoder *self = (SDecoder*)data;
	unsigned int nchannels, nsamples;
	mad_fixed_t const *left_ch, *right_ch;

	nchannels = pcm->channels;
	nsamples  = pcm->length;
	left_ch   = pcm->samples[0];
	right_ch  = pcm->samples[1];

	while ( nsamples>0 ) {
		int i;
		Data *data = self->data[self->writeto];
		pthread_mutex_lock(&data->mutex); // enter the critical region

		/* wait until the buffer becomes readable!  */
		while (data->state!=WRITABLE) {
			pthread_cond_wait(&data->cond, &data->mutex);
			//if (!runnable) {
				// TODO
				//pthread_mutex_unlock(&data->mutex); // leave the critical region
				//break;
			//}
		}

		i = data->cur_offset;
		while ( nsamples>0 && i<data->allocated_size-4 ) {
			signed int sample;

			/* output sample(s) in 16-bit signed little-endian PCM */
			sample = scale(*left_ch++);
			data->data[i++] = ((sample >> 0) & 0xff);
			data->data[i++] = ((sample >> 8) & 0xff);

			// TODO
			sample = scale(*right_ch++);
			data->data[i++] = ((sample >> 0) & 0xff);
			data->data[i++] = ((sample >> 8) & 0xff);

			/*
			for (j=i-4; j<i; j++) {
				if (data->data[j]!=0)
					printf("%x",data->data[j]);
			}
			*/

			nsamples--;
		}

		data->cur_offset = 0;
		data->state = READABLE;
		data->size = i;
		self->writeto = 1 - self->writeto;
		pthread_cond_signal(&data->cond);
		pthread_mutex_unlock(&data->mutex); // leave the critical region
	}

	return MAD_FLOW_CONTINUE;
}

static
enum mad_flow mad_error(void *data,
		struct mad_stream *stream,
		struct mad_frame *frame)
{

	fprintf(stderr, "mad_error: 0x%04x (%s) at byte offset %p\n",
			stream->error, mad_stream_errorstr(stream), stream->this_frame);

	fprintf(stderr, "mad_stream {\n"
			"\tbuffer %p\n"
			"\tskiplen %lu\n"
			"\tthis_frame %p\n"
			"\tnext_frame %p\n"
			"\terror %u\n"
			"}\n",
		   stream->buffer, stream->skiplen, stream->this_frame, stream->next_frame, stream->error);

	/* return MAD_FLOW_BREAK here to stop decoding (and propagate an error) */
	//return MAD_FLOW_BREAK;
	return MAD_FLOW_CONTINUE;
}

#if 0
struct mad_stream {
	unsigned char const *buffer;          /* input bitstream buffer */
	unsigned char const *bufend;          /* end of buffer */
	unsigned long skiplen;                /* bytes to skip before next frame */

	int sync;                             /* stream sync found */
	unsigned long freerate;               /* free bitrate (fixed) */

	unsigned char const *this_frame;      /* start of current frame */
	unsigned char const *next_frame;      /* start of next frame */
	struct mad_bitptr ptr;                /* current processing bit pointer */

	struct mad_bitptr anc_ptr;            /* ancillary bits pointer */
	unsigned int anc_bitlen;              /* number of ancillary bits */

	unsigned char (*main_data)[MAD_BUFFER_MDLEN];
	/* Layer III main_data() */
	unsigned int md_len;                  /* bytes in main_data */

	int options;                          /* decoding options (see below) */
	enum mad_error error;                 /* error code (see above) */
};
#endif


static PyObject *
SDecoder_name(SDecoder *self)
{
    static PyObject *format = NULL;
    PyObject *args, *result;

    if (format==NULL) {
        format = PyUnicode_FromString("%d");
        if (format == NULL)
            return NULL;
    }

    args = Py_BuildValue("i", self->bufsiz);
    if (args==NULL)
        return NULL;

    result = PyUnicode_Format(format, args);
    Py_DECREF(args);

    return result;
}

PyMemberDef SDecoder_members[] = {
	//{"first", T_OBJECT_EX, offsetof(Noddy,first), 0, "first name" },
	//{"last", T_OBJECT_EX, offsetof(Noddy,last), 0, "last name" },
	//{"number", T_INT, offsetof(Noddy, number), 0, "noddy number" },
	{"number", T_INT, offsetof(SDecoder, bufsiz), 0, "noddy number" },
	{NULL}
};
PyMethodDef SDecoder_methods[] = {
	{"run", (PyCFunction)SDecoder_run, METH_NOARGS,
		"Start decoding on another thread." },
	{"getPCM", (PyCFunction)SDecoder_getPCM, METH_VARARGS,
		"Return PCM data from mad decoder." },
	{"config", (PyCFunction)SDecoder_getPCM, METH_VARARGS,
		"configure formats for samplerate, channels and bits" },
    {"name", (PyCFunction)SDecoder_name, METH_NOARGS,
        "Return the name, combining the first and last name" },
	{NULL}
};

PyTypeObject SDecoderType = {
	PyVarObject_HEAD_INIT(NULL,0)
	"sotetsu.madsimple.SimpleDecoder",
	sizeof(SDecoder),
	0,
	(destructor)SDecoder_dealloc,
    0,                         /* tp_print */
    0,                         /* tp_getattr */
    0,                         /* tp_setattr */
    0,                         /* tp_reserved */
    0,                         /* tp_repr */
    0,                         /* tp_as_number */
    0,                         /* tp_as_sequence */
    0,                         /* tp_as_mapping */
    0,                         /* tp_hash  */
    0,                         /* tp_call */
    0,                         /* tp_str */
    0,                         /* tp_getattro */
    0,                         /* tp_setattro */
    0,                         /* tp_as_buffer */
    Py_TPFLAGS_DEFAULT |
        Py_TPFLAGS_BASETYPE,   /* tp_flags */
    "madsimple's decoder objects",           /* tp_doc */
    0,		               /* tp_traverse */
    0,		               /* tp_clear */
    0,		               /* tp_richcompare */
    0,		               /* tp_weaklistoffset */
    0,		               /* tp_iter */
    0,		               /* tp_iternext */
    SDecoder_methods,             /* tp_methods */
    SDecoder_members,             /* tp_members */
    0,                         /* tp_getset */
    0,                         /* tp_base */
    0,                         /* tp_dict */
    0,                         /* tp_descr_get */
    0,                         /* tp_descr_set */
    0,                         /* tp_dictoffset */
    (initproc)SDecoder_init,      /* tp_init */
    0,                         /* tp_alloc */
    SDecoder_new,                 /* tp_new */
};

